package com.qh.pay.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.domain.IpAddrLbsInfo;
import com.qh.common.config.CfgKeyConst;
import com.qh.common.config.Constant;
import com.qh.common.service.IpAddressService;
import com.qh.common.utils.R;
import com.qh.pay.api.Order;
import com.qh.pay.api.PayApi;
import com.qh.pay.api.PayConstants;
import com.qh.pay.api.constenum.*;
import com.qh.pay.api.utils.DateUtil;
import com.qh.pay.api.utils.ParamUtil;
import com.qh.pay.api.utils.RSAUtil;
import com.qh.pay.api.utils.RequestUtils;
import com.qh.pay.dao.*;
import com.qh.pay.domain.*;
import com.qh.pay.service.*;
import com.qh.redis.RedisConstants;
import com.qh.redis.service.RedisMsg;
import com.qh.redis.service.RedisUtil;
import com.qh.redis.service.RedissonLockUtil;
import com.qh.sms.service.SMSUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.util.IPAddress;
import org.redisson.api.RLock;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @version 1.0.0
 * @ClassName PayQrServiceImpl
 * @Description 扫码通道实现类
 * @Date 2017年12月19日 上午10:27:30
 */
@Service
public class PayQrServiceImpl implements PayQrService {
    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(PayQrServiceImpl.class);
    @Autowired
    private PayQrConfigService payQrConfigService;
    @Autowired
    private PayOrderDao payOrderDao;
    @Autowired
    private PayOrderQrDao payOrderQrDao;
    @Autowired
    private PayHandlerService payHandlerService;
    @Autowired
    private RecordFoundAcctDao rdFoundAcctDao;
    @Autowired
    private RecordFoundAvailAcctDao rdFoundAvailAcctDao;
    @Autowired
    private MerchChargeDao merchChargeDao;
    @Autowired
    private RecordMerchBalDao rdMerchBalDao;
    @Autowired
    private RecordMerchAvailBalDao rdMerchAvailBalDao;
    @Autowired
    private MerchantService merchantService;
    @Autowired
    private PayOrderLoseDao payOrderLoseDao;



    /* (非 Javadoc)
     * Description:
     * @see com.qh.pay.service.PayQrService#getChargeMon(java.lang.String, java.lang.String)
     */
    @Override
    public R getChargeMon(String monAmount, String merchNo, String outChannel) {
        PayAcctBal pab = RedisUtil.getPayFoundBal();
        String pabFoundNo = pab.getUsername();
        Set<Object> accountNos = payQrConfigService.findAccountNo(outChannel);
        if (CollectionUtils.isEmpty(accountNos)) {
            return R.error(merchNo + "未配置相应的扫码通道");
        }
        PayQrConfigDO payQrCfg = this.findAccountNo(outChannel, accountNos);
        if (payQrCfg == null) {
            return R.error("未找到相应的有效的扫码通道");
        }
        //资金账户下的金额 放置对应的充值商户号
//        String newMonAmount = findCloseMonAmount(monAmount, merchNo, payQrCfg);
//        if (ParamUtil.isEmpty(newMonAmount)) {
//            return R.error(merchNo + "," + monAmount + "支付金额被占用或不存在");
//        }

        Map<String, String> resultMap = new HashMap<>();
//        resultMap.put("amount", newMonAmount);
        resultMap.put("accountNo", payQrCfg.getAccountNo());
//        if(payQrCfg != null && payQrCfg.getQrs().containsKey(newMonAmount)){
//            resultMap.put("qr_url", "/files/" + pabFoundNo + "/" + outChannel + "/" + payQrCfg.getAccountNo() + "/"
//            + newMonAmount.replace(".", "p") + ".jpg?r=" + DateUtil.getCurrentTimeInt());
//        }else{
//            resultMap.put("qr_url", "/files/" + pabFoundNo + "/" + outChannel + "/" + payQrCfg.getAccountNo() + "/0
//            .jpg?r=" + DateUtil.getCurrentTimeInt());
//        }
//        int remainSec = RedisUtil.getMonAmountOccupyValidTime(outChannel, payQrCfg.getAccountNo(),
//                newMonAmount);
//        resultMap.put("remainSec", String.valueOf(remainSec));
        return R.okData(resultMap);
    }

    private PayQrConfigDO findAccountNo(String outChannel, Set<Object> accountNos) {
        List<PayQrTotalMoney> qrTotalMoneys = new ArrayList<>();
        Map<Object, Boolean> map = new HashMap<>();
        //先检测通道是否存在

        int ran1 = new Random().nextInt(accountNos.size()) + 1;
        int ran2 = 0;

        for (Object obj : accountNos) {
            ran2++;
            if (ran1 == ran2) {
                return payQrConfigService.get(outChannel, (String) obj);
            }

        }
        //只遍历开启的通道
        return null;
    }

//    private PayQrConfigDO findAccountNo(String outChannel, Set<Object> accountNos) {
//        List<PayQrTotalMoney> qrTotalMoneys = new ArrayList<>();
//        Map<Object, Boolean> map = new HashMap<>();
//        //先检测通道是否存在
//        for (Object obj : accountNos) {
//
//            if(checkGatewayIsAlive(outChannel,(String)obj)){
//                map.put(obj,true);
//            }else{
//                PayQrConfigDO payQrCfg = payQrConfigService.get(outChannel,(String)obj);
//                SMSUtils.sendMessageNotify(payQrCfg.getAccountPhone(), OutChannel.jfDesc().get(outChannel));
//            }
//        }
//        //只遍历开启的通道
//        for (Object obj : map.keySet()) {
//            PayQrTotalMoney qrTotalMoney =
//                    (PayQrTotalMoney) RedisUtil.getRedisTemplate().opsForHash().get(RedisConstants
//                    .cache_qr_total_money + outChannel, (String) obj);
//            if (qrTotalMoney == null) {
//                return payQrConfigService.get(outChannel, (String) obj);
//            } else {
//                qrTotalMoneys.add(qrTotalMoney);
//            }
//        }
//        if (CollectionUtils.isNotEmpty(qrTotalMoneys)) {
//            Collections.sort(qrTotalMoneys, new Comparator<PayQrTotalMoney>() {
//                @Override
//                public int compare(PayQrTotalMoney o1, PayQrTotalMoney o2) {
//                    return o1.getTotalMoney().compareTo(o2.getTotalMoney());
//                }
//            });
//            int size = qrTotalMoneys.size();
//            PayQrTotalMoney qrTotalMoney;
//            for (int i = 0; i < size; i++) {
//                qrTotalMoney = qrTotalMoneys.get(i);
//                return payQrConfigService.get(outChannel, qrTotalMoney.getAccountNo());
//            }
//        }
//        return null;
//    }

    /* (非 Javadoc)
     * Description:
     * @see com.qh.pay.service.PayQrService#qrOrder(com.qh.pay.api.Order)
     */
    @Override
    public R qrOrder(Order order, Merchant merchant) {
        String outChannel = order.getOutChannel();
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("payStatus", "0");
        hashMap.put("outChannel", outChannel);
        hashMap.put("useStaus", 1);
        hashMap.put("money", order.getAmount());
        List<PayQrConfigDO> payQrConfigDOS = payQrConfigService.list(hashMap);

        if (CollectionUtils.isEmpty(payQrConfigDOS)) {
            return R.error("无可用通道，请联系新增通道");
        }
        PayQrConfigDO payQrCfg = null;

        //打乱顺序，随机读取一条通道
        Collections.shuffle(payQrConfigDOS);
        for (PayQrConfigDO payQrConfigDOS1 : payQrConfigDOS) {

            if (LocalDateTime.now().plusMinutes(6).isBefore(payQrConfigDOS1.getEndDate())) {

                RLock lock = RedissonLockUtil.getMonAmountLock(payQrConfigDOS1.getOutChannel(),
                        payQrConfigDOS1.getAccountNo());
                try {
                    lock.lock();
                    if (!RedisUtil.ifMonAmountOccupy(payQrConfigDOS1.getOutChannel(), payQrConfigDOS1
                            .getAccountNo())) {
                        int i =  PayApi.pay(payQrConfigDOS1);
                        if(i==1){
                            //减少可用通道数量
                            RedisUtil.updateMonAmountNum(payQrConfigDOS1.getOutChannel(),
                                    String.valueOf(payQrConfigDOS1.getMoney()),false);
                            RedisUtil.setMonAmountOccupy(payQrConfigDOS1.getOutChannel(), payQrConfigDOS1
                                    .getAccountNo());
                            RedisUtil.setMonAmountOrderNo(payQrConfigDOS1.getOutChannel(), payQrConfigDOS1
                                            .getAccountNo(),
                                    merchant.getMerchNo() + RedisConstants.link_symbol + order.getOrderNo());
                            payQrCfg = payQrConfigDOS1;
                            break;
                       }else if(i==2){
                            RedisUtil.updateMonAmountNum(payQrConfigDOS1.getOutChannel(),
                                    String.valueOf(payQrConfigDOS1.getMoney()),false);
                            payQrConfigService.updateQrPayState("1",payQrConfigDOS1.getAccountNo(),payQrConfigDOS1.getOutChannel());
                            new RedisUtil().paystatus( payQrConfigDOS1);
                        }else{
                            RedisUtil.updateMonAmountNum(payQrConfigDOS1.getOutChannel(),
                                    String.valueOf(payQrConfigDOS1.getMoney()),false);
                            payQrConfigService.updateQrPayState("2",payQrConfigDOS1.getAccountNo(),payQrConfigDOS1.getOutChannel());
                        }

                    }
                } finally {
                    lock.unlock();
                }
            }
        }
        //判断是否在支付范围内
        if (Objects.isNull(payQrCfg)) {
            return R.error("通道被占用，请联系管理员新增通道");
        }


        order.setPayMerch(payQrCfg.getAccountNo());
        order.setRealAmount(order.getAmount());
        order.setQhAmount(BigDecimal.ZERO);
//        //判断商户的可用余额

        if (ParamUtil.isNotEmpty(order.getMsg()) && order.getMsg().
                length() > 50) {
            order.setMsg(order.getMsg().substring(0, 50));
        }
        order.setCrtDate(DateUtil.getCurrentTimeInt());
        Map<String, String> resultMap = PayService.initRspData(order);
        try {
            String qrUrl = payQrCfg.getQrs();
            if (StringUtils.isEmpty(qrUrl)) {
                logger.error("收款码未找到");
                return R.error("收款码异常");
            }
            //支付扫码通道
//            resultMap.put(PayConstants.qr_url, qrUrl);
            resultMap.put(PayConstants.web_code_url, PayService.commonQrUrl(order));
            resultMap.put(PayConstants.qr_url, payQrCfg.getQrs());
            //返回真正的金额
            resultMap.put(OrderParamKey.amount.name(), order.getAmount().toPlainString());
        } catch (
                Exception e) {
            e.printStackTrace();
            logger.error("支付扫码url加密异常");
            return R.error("加密异常");
        }
        return R.okData(resultMap);
    }


    /**
     * @Description 与网关通讯间隔时间超出时间 320S 则返回false
     * @Author chensi
     * @Time 2017/12/20 19:29
     */
    private boolean checkGatewayIsAlive(String outChannel, String accountNo) {
        Long oldTime = (Long) RedisUtil.getQrGatewayLastSyncTime(outChannel, accountNo);
        logger.info("    " + outChannel + "     " + accountNo + "      " + oldTime);
        if (oldTime == null) {
            return false;
        }
        long nowTime = new Date().getTime();
        long timeDif = nowTime - oldTime;
        logger.info("" + timeDif);
        if (timeDif > 1000 * 320 || timeDif <= 0) {
            return false;
        }
        return true;
    }

    /**
     * @param monAmount
     * @param orderNo
     * @param payQrCfg
     * @return
     * @Description 找到最接近的金额数值
     */
    private String findCloseMonAmount(String monAmount, String merchno, String orderNo, PayQrConfigDO payQrCfg) {
        int count = 100;
        boolean findFlag = false;
        monAmount = ParamUtil.subZeroAndDot(monAmount);
        for (int i = 0; i < count; i++) {
            RLock monAmountLock = RedissonLockUtil.getMonAmountLock(payQrCfg.getOutChannel()
                    , payQrCfg.getAccountNo());
            try {
                monAmountLock.lock();
                if (!RedisUtil.ifMonAmountOccupy(payQrCfg.getOutChannel(), payQrCfg
                        .getAccountNo())) {
                    findFlag = true;
                    RedisUtil.setMonAmountOccupy(payQrCfg.getOutChannel(), payQrCfg
                            .getAccountNo());
                    RedisUtil.setMonAmountOrderNo(payQrCfg.getOutChannel(), payQrCfg
                            .getAccountNo(), merchno + RedisConstants.link_symbol + orderNo);
                }
            } finally {
                monAmountLock.unlock();
            }
            if (findFlag) {
                return monAmount;
            }
            monAmount = ParamUtil.delMinMonAmount(monAmount);
        }
        return null;
    }

    /**
     * @param monAmount
     * @param orderNo
     * @param payQrCfg
     * @return
     * @Description 找到最接近的金额数值
     */
    private String findCloseMonAmountnot(String monAmount, String merchno, String orderNo, PayQrConfigDO payQrCfg) {
        int count = 100;
        boolean findFlag = false;
        monAmount = ParamUtil.subZeroAndDot(monAmount);
        for (int i = 0; i < count; i++) {
            RLock monAmountLock = RedissonLockUtil.getMonAmountLock(payQrCfg.getOutChannel()
                    , payQrCfg.getAccountNo());
            try {
                monAmountLock.lock();
                if (!RedisUtil.ifMonAmountOccupy(payQrCfg.getOutChannel(), payQrCfg
                        .getAccountNo())) {
                    findFlag = true;
//                    RedisUtil.setMonAmountOccupy(payQrCfg.getOutChannel(), payQrCfg
//                            .getAccountNo(), monAmount);
//                    RedisUtil.setMonAmountOrderNo(payQrCfg.getOutChannel(), payQrCfg
//                            .getAccountNo(), monAmount, merchno + RedisConstants.link_symbol + orderNo);
                }
            } finally {
                monAmountLock.unlock();
            }
            if (findFlag) {
                return monAmount;
            }
            monAmount = ParamUtil.delMinMonAmount(monAmount);
        }
        return null;
    }

    /* (非 Javadoc)
     * Description:
     * @see com.qh.pay.service.PayQrService#releaseMonAmount(com.qh.pay.api.Order, com.qh.pay.domain.Merchant)
     */
    @Override
    public void releaseMonAmount(Order order) {
        String monAmount = order.getRealAmount().toPlainString();
        RLock monAmountLock = RedissonLockUtil.getMonAmountLock(order.getOutChannel(),
                order.getPayMerch());
        try {
            monAmountLock.lock();
            RedisUtil.delMonAmountOccupy(order.getOutChannel(), order.getPayMerch());
            RedisUtil.delMonAmountOrderNo(order.getOutChannel(), order.getPayMerch());
        } finally {
            monAmountLock.unlock();
        }
    }

    /* (非 Javadoc)
     * Description:
     * @see com.qh.pay.service.PayQrService#notifyQr(java.lang.String, java.lang.String, java.lang.String, java.lang
     * .String)
     */
    @Override
    public void notifyQr(String merchNo, String outChannel, String accountNo, String monAmount, String businessNo,
                         String msg) {

        monAmount = ParamUtil.subZeroAndDot(monAmount);
        RLock monAmountLock = RedissonLockUtil.getMonAmountLock(outChannel, accountNo);
        try {
            monAmountLock.lock();
            if (!RedisUtil.ifQrBusinessNo(outChannel, accountNo, businessNo)) {//该支付业务单号已经处理
                logger.warn("该支付业务单号已经处理,{}", businessNo);
                return;
            }
            String orderNo = RedisUtil.getMonAmountOrderNo(outChannel, accountNo);
            if (ParamUtil.isEmpty(orderNo)) {
                logger.error("该支付订单号不存在,{}", orderNo);
                this.saveQrOrderLoseData(outChannel, accountNo, monAmount, businessNo, msg, null);
                return;
            }
            Order order = RedisUtil.getOrder(merchNo, orderNo);
            if (ParamUtil.isEmpty(order) || DateUtil.getCurrentTimeInt() > order.getCrtDate() + 300) {
                logger.error("该支付订单不存在,{},{}", merchNo, orderNo);
                this.saveQrOrderLoseData(outChannel, accountNo, monAmount, businessNo, msg, null);
                return;
            }
            if (order.getRealAmount().compareTo(new BigDecimal(monAmount)) != 0) {
                logger.error("金额不一致,订单金额：{},回调金额：{}", order.getRealAmount().toPlainString(), monAmount);
                return;
            }
            //更新支付信息
            order.setBusinessNo(businessNo);
            order.setOrderState(OrderState.succ.id());
            if (ParamUtil.isNotEmpty(msg) && msg.length() > 50) {
                order.setMsg(msg.substring(0, 50));
            } else {
                order.setMsg(msg);
            }

            RedisUtil.setOrder(order);
            RedisMsg.orderNotifyMsg(merchNo, orderNo);
            RedisUtil.setQrBusinessNo(outChannel, accountNo, businessNo);
            RedisUtil.delMonAmountOccupy(outChannel, accountNo);
            RedisUtil.delMonAmountOrderNo(outChannel, accountNo);
            this.qrTotalMoney(merchNo, outChannel, accountNo, monAmount);
        } finally {
            monAmountLock.unlock();
        }
    }

    private String find(String outChannel, String accountNo, String monAmount) {

        int j = 0;
        String chargeMerchNo = null;
        for (int i = 0; i < 5; i++) {
            chargeMerchNo = RedisUtil.getMonAmountOrderNo(outChannel, accountNo);
            if (i == 0) {
                if (!ParamUtil.isEmpty(chargeMerchNo)) {
                    return chargeMerchNo;
                }
            } else {
                if (!ParamUtil.isEmpty(chargeMerchNo)) {
                    j++;
                }
            }
            monAmount = ParamUtil.delMinMonAmount(monAmount);
        }
        if (j > 1) {
            return null;
        }
        return chargeMerchNo;
    }
    @Override
    public void notifyQr(Order order) {
        saveQrOrderQrData(order.getOutChannel(), order.getPayMerch(), order.getAmount().toPlainString(),
                order.getPayMerch(), order.getPayMerch());
        RLock monAmountLock = RedissonLockUtil.getMonAmountLock(order.getOutChannel(), order.getPayMerch());
        try {
            monAmountLock.lock();
            //更新支付信息
            order.setBusinessNo(order.getPayMerch());
            order.setOrderState(OrderState.succ.id());
            if (ParamUtil.isNotEmpty(order.getMsg()) && order.getMsg().length() > 50) {
                order.setMsg(order.getMsg().substring(0, 50));
            } else {
                order.setMsg(order.getMsg());
            }
            order.setNoticeState("0");
            RedisUtil.setOrder(order);
            RedisMsg.orderNotifyMsg(order.getMerchNo(), order.getOrderNo());
            RedisUtil.setQrBusinessNo(order.getMerchNo(), order.getPayMerch(), order.getBusinessNo());
            this.qrTotalMoney(order.getMerchNo(), order.getOutChannel(), order.getPayMerch(), order.getAmount().toPlainString());
        } finally {
            RedisUtil.delMonAmountOccupy( order.getOutChannel(), order.getPayMerch());
            RedisUtil.delMonAmountOrderNo( order.getOutChannel(), order.getPayMerch());
            monAmountLock.unlock();
        }
    }
    @Override
    public void notifyQr(String outChannel, String accountNo, String monAmount,
                         String msg, String date) {
        String businessNo = UUID.randomUUID().toString().replaceAll("-", "");
//        String chargeMerchNo = RedisUtil.getMonAmountOrderNo(outChannel, accountNo, monAmount);
        String chargeMerchNo = find(outChannel, accountNo, monAmount);
        saveQrOrderQrData(outChannel, accountNo, monAmount,
                businessNo, chargeMerchNo);
        if (ParamUtil.isEmpty(chargeMerchNo)) {
            logger.error("该支付订单号不存在,{}", chargeMerchNo);
            this.saveQrOrderLoseData(outChannel, accountNo, monAmount, businessNo, msg, date);
            return;
        }
        String merchNo = chargeMerchNo.split(RedisConstants.link_symbol)[0];
        String orderNo = chargeMerchNo.split(RedisConstants.link_symbol)[1];
        monAmount = ParamUtil.subZeroAndDot(monAmount);
        RLock monAmountLock = RedissonLockUtil.getMonAmountLock(outChannel, accountNo);
        try {
            monAmountLock.lock();
//            if (!RedisUtil.ifQrBusinessNo(merchNo, outChannel, accountNo, businessNo)) {//该支付业务单号已经处理
//                logger.warn("该支付业务单号已经处理,{}", businessNo);
//                return;
//            }
//            String orderNo = RedisUtil.getMonAmountOrderNo(outChannel, accountNo, monAmount);
//            if (ParamUtil.isEmpty(orderNo)) {
//                logger.error("该支付订单号不存在,{}", orderNo);
//                this.saveQrOrderLoseData(merchNo, outChannel, accountNo, monAmount, businessNo, msg);
//                return;
//            }
            Order order = RedisUtil.getOrder(merchNo, orderNo);
//            Order order = RedisUtil.getOrder(merchNo, orderNo);
            if (ParamUtil.isEmpty(order) || DateUtil.getCurrentTimeInt() > order.getCrtDate() + 300) {
                logger.error("该支付订单不存在,{}", chargeMerchNo);
                this.saveQrOrderLoseData(outChannel, accountNo, monAmount, businessNo, msg, date);
                return;
            }

            //更新支付信息
            order.setBusinessNo(businessNo);
            order.setOrderState(OrderState.succ.id());
            if (ParamUtil.isNotEmpty(msg) && msg.length() > 50) {
                order.setMsg(msg.substring(0, 50));
            } else {
                order.setMsg(msg);
            }

            RedisUtil.setOrder(order);
            RedisMsg.orderNotifyMsg(merchNo, orderNo);
            RedisUtil.setQrBusinessNo(outChannel, accountNo, businessNo);
            //通知商户后台
//            orderNotify(order);
            this.qrTotalMoney(merchNo, outChannel, accountNo, monAmount);
        } finally {
            RedisUtil.delMonAmountOccupy(outChannel, accountNo);
            RedisUtil.delMonAmountOrderNo(outChannel, accountNo);
            monAmountLock.unlock();
        }
    }
//
//    @Override
//    public void notifyQr(String outChannel, String accountNo, String monAmount,
//                         String msg) {
//        String businessNo = UUID.randomUUID().toString().replaceAll("-", "");
//
//        String chargeMerchNo = RedisUtil.getMonAmountOrderNo(outChannel, accountNo, monAmount);
//        saveQrOrderQrData(outChannel, accountNo, monAmount,
//                businessNo, chargeMerchNo);
//        if (ParamUtil.isEmpty(chargeMerchNo)) {
//            logger.error("该支付订单号不存在,{}", chargeMerchNo);
//            this.saveQrOrderLoseData(outChannel, accountNo, monAmount, businessNo, msg);
//            return;
//        }
//        String merchNo = chargeMerchNo.split(RedisConstants.link_symbol)[0];
//        String orderNo = chargeMerchNo.split(RedisConstants.link_symbol)[1];
//        monAmount = ParamUtil.subZeroAndDot(monAmount);
//        RLock monAmountLock = RedissonLockUtil.getMonAmountLock(outChannel, accountNo, monAmount);
//        try {
//            monAmountLock.lock();
////            if (!RedisUtil.ifQrBusinessNo(merchNo, outChannel, accountNo, businessNo)) {//该支付业务单号已经处理
////                logger.warn("该支付业务单号已经处理,{}", businessNo);
////                return;
////            }
////            String orderNo = RedisUtil.getMonAmountOrderNo(outChannel, accountNo, monAmount);
////            if (ParamUtil.isEmpty(orderNo)) {
////                logger.error("该支付订单号不存在,{}", orderNo);
////                this.saveQrOrderLoseData(merchNo, outChannel, accountNo, monAmount, businessNo, msg);
////                return;
////            }
//            Order order = RedisUtil.getOrder(merchNo, orderNo);
////            Order order = RedisUtil.getOrder(merchNo, orderNo);
//            if (ParamUtil.isEmpty(order) || DateUtil.getCurrentTimeInt() > order.getCrtDate() + 300) {
//                logger.error("该支付订单不存在,{}", chargeMerchNo);
//                this.saveQrOrderLoseData(outChannel, accountNo, monAmount, businessNo, msg);
//                return;
//            }
//
//            //更新支付信息
//            order.setBusinessNo(businessNo);
//            order.setOrderState(OrderState.succ.id());
//            if (ParamUtil.isNotEmpty(msg) && msg.length() > 50) {
//                order.setMsg(msg.substring(0, 50));
//            } else {
//                order.setMsg(msg);
//            }
//
//            RedisUtil.setOrder(order);
//            RedisMsg.orderNotifyMsg(merchNo, orderNo);
//            RedisUtil.setQrBusinessNo(outChannel, accountNo, businessNo);
//            //通知商户后台
//            orderNotify(order);
////            payService.orderDataMsg(merchNo, orderNo);
//            this.qrTotalMoney(merchNo, outChannel, accountNo, monAmount);
//        } finally {
//
//            RedisUtil.delMonAmountOccupy(outChannel, accountNo, monAmount);
//            RedisUtil.delMonAmountOrderNo(outChannel, accountNo, monAmount);
//            monAmountLock.unlock();
//        }
//    }
//

    /**
     * @param order
     * @return
     * @Description 订单通知
     */
    private String orderNotify(Order order) {
        if (order == null) {
            return null;
        }
        String stateDesc = OrderState.desc().get(order.getOrderState());
        String result = null;
        logger.info("发送通知请求：{},{},{},{}", order.getNotifyUrl(), order.getMerchNo(), order.getOrderNo(), stateDesc);
        if (OrderState.init.id() == order.getOrderState()) {
            //RequestUtils.doPostJson(order.getNotifyUrl(), R.error(order.getMsg()).jsonStr());
            logger.info("{}状态返回结果：{},{},{}", stateDesc, order.getMerchNo(), order.getOrderNo(), result);
        } else {
            Map<String, String> data = PayService.initRspData(order);
            data.put(OrderParamKey.orderState.name(), String.valueOf(order.getOrderState()));
            data.put(OrderParamKey.businessNo.name(), order.getBusinessNo());
            data.put(OrderParamKey.amount.name(), order.getRealAmount() == null ? order.getAmount().toString() :
                    order.getRealAmount().toString());
            data.put(OrderParamKey.orderNo.name(), order.getOrderNo());
            Merchant merchant = merchantService.get(order.getMerchNo());
            result = RequestUtils.doPostJson(order.getNotifyUrl(),
                    decryptAndSign(data, merchant.getPublicKey(), order.getMsg()).jsonStr());
            logger.info("{}状态返回结果：{},{},{}", stateDesc, order.getMerchNo(), order.getOrderNo(), result);
            if (result.contains("ok") || result.contains("success")) {
                result = Constant.result_msg_ok;
            }
        }
        return result;
    }

    /**
     * @param data
     * @return
     * @Description 公钥加密，私钥签名
     */
    private R decryptAndSign(Map<String, ?> data, String publicKey, String msg) {
        try {
            logger.info("回调商户 返回明文数据:" + JSON.toJSONString(data));
            String merchNo = data.get("merchNo").toString();
            R r = R.ok();
            byte[] context = RSAUtil.encryptByPublicKey(JSON.toJSONBytes(data), publicKey);
            String privateKey = merchantService.getPrivateKey(merchNo);
            String sign = RSAUtil.sign(context, privateKey);
            r.put("sign", sign).put("context", context);
            r.put(Constant.result_msg, msg).put("merchNo", merchNo).put("orderNo", data.get("orderNo"));
            logger.info("回调商户 返回加密数据:" + JSON.toJSONString(r));
            return r;
        } catch (Exception e) {
            logger.error("返回数据签名 失败！");
        }
        return R.error("返回数据签名失败！");
    }


    private void qrTotalMoney(String merchNo, String outChannel, String accountNo, String monAmount) {
        RLock qrTotalMoneyLock = RedissonLockUtil.getMonAmountLock(outChannel, accountNo);
        try {
            qrTotalMoneyLock.lock(5, TimeUnit.SECONDS);
            PayQrTotalMoney payQrTotalMoney =
                    (PayQrTotalMoney) RedisUtil.getRedisTemplate().opsForHash().get(RedisConstants.cache_qr_total_money + outChannel, accountNo);
            if (payQrTotalMoney == null) {
                payQrTotalMoney = new PayQrTotalMoney();
                payQrTotalMoney.setOutChannel(outChannel);
                payQrTotalMoney.setMerchNo(merchNo);
                payQrTotalMoney.setAccountNo(accountNo);
                payQrTotalMoney.setTotalMoney(new BigDecimal(monAmount));
            } else {
                payQrTotalMoney.setTotalMoney(payQrTotalMoney.getTotalMoney().add(new BigDecimal(monAmount)));
            }
            RedisUtil.getRedisTemplate().opsForHash().put(RedisConstants.cache_qr_total_money + outChannel, accountNo
                    , payQrTotalMoney);

        } finally {
            qrTotalMoneyLock.unlock();
        }
    }

    /**
     * @param outChannel
     * @param monAmount
     * @param businessNo
     * @param msg
     * @Description 保存掉单数据
     */
    @Override
    public void saveQrOrderLoseData(String outChannel, String accountName, String monAmount,
                                     String businessNo,
                                     String msg, String date) {
        Order order = payOrderLoseDao.getByBusinessNo(businessNo, outChannel);
        if (order != null) {
            return;
        }
        order = new Order();
        order.setOrderNo("0");
        order.setOutChannel(outChannel);
        order.setAmount(new BigDecimal(monAmount));
        order.setQhAmount(BigDecimal.ZERO);
        order.setBusinessNo(businessNo);
        order.setMerchNo(accountName);
        if (ParamUtil.isNotEmpty(msg) && msg.length() > 50) {
            order.setMsg(msg.substring(0, 50));
        } else {
            order.setMsg(msg);
        }
        order.setOrderState(OrderState.init.id());
        order.setOrderType(OrderType.pay.id());
        int dateStr = DateUtil.getCurrentTimeInt();
        if (!StringUtils.isEmpty(date)) {
            if (DateUtil.parseDateTime(date) != null) {
                dateStr = (int) (DateUtil.parseDateTime(date).getTime() / 1000);
            }

        }
        order.setCrtDate(dateStr);
        order.setPayMerch(businessNo);
        payOrderLoseDao.save(order);
    }

    /**
     * @param outChannel
     * @param monAmount
     * @param businessNo
     * @Description 保存支付数据
     */
    private void saveQrOrderQrData(String outChannel, String accountNo, String monAmount,
                                   String businessNo, String orderNo) {
        Order order = payOrderQrDao.getByBusinessNo(businessNo);
        if (order != null) {
            return;
        }
        order = new Order();
        order.setOrderNo(orderNo);
        order.setOutChannel(outChannel);
        order.setMobile(accountNo);
        order.setAmount(new BigDecimal(monAmount));
        order.setBusinessNo(businessNo);
        order.setCrtDate(DateUtil.getCurrentTimeInt());
        payOrderQrDao.save(order);
    }

    /* (非 Javadoc)
     * Description:
     * @see com.qh.pay.service.PayQrService#notifyChargeQr(java.lang.String, java.lang.String, java.lang.String, java
     * .lang.String)
     */
    @Override
    public void notifyChargeQr(String merchNo, String outChannel, String monAmount, String accountNo,
                               String businessNo, String msg) {
        monAmount = ParamUtil.subZeroAndDot(monAmount);
        RLock monAmountLock = RedissonLockUtil.getMonAmountLock(outChannel, accountNo);
        try {
            monAmountLock.lock();
            if (!RedisUtil.ifQrBusinessNo(outChannel, accountNo, businessNo)) {//该支付业务单号已经处理
                logger.warn("该充值业务单号已经处理,{}", businessNo);
                return;
            }
            String chargeMerchNo = RedisUtil.getMonAmountOrderNo(outChannel, accountNo);
            if (ParamUtil.isEmpty(chargeMerchNo)) {
                logger.error("该充值商户不存在,{}", chargeMerchNo);
                return;
            }
            //更新充值信息
            MerchCharge merchCharge = PayQrService.initMerchCharge(chargeMerchNo, outChannel, monAmount, businessNo);
            merchCharge.setClearState(ClearState.succ.id());
            merchCharge.setOrderState(OrderState.succ.id());
            merchCharge.setAccountNo(accountNo);

            if (ParamUtil.isNotEmpty(msg) && msg.length() > 50) {
                merchCharge.setMsg(msg.substring(0, 50));
            } else {
                merchCharge.setMsg(msg);
            }
            RedisUtil.setMerchCharge(merchCharge);
            RedisMsg.chargeDataMsg(chargeMerchNo, businessNo);

            RedisUtil.setQrBusinessNo(outChannel, accountNo, businessNo);
            RedisUtil.delMonAmountOccupy(outChannel, accountNo);
            RedisUtil.delMonAmountOrderNo(outChannel, accountNo);
            this.qrTotalMoney(merchNo, outChannel, accountNo, monAmount);
        } finally {
            monAmountLock.unlock();
        }

    }

    /* (非 Javadoc)
     * Description:商户充值无手续费，无代理费率
     * @see com.qh.pay.service.PayQrService#chargeDataMsg(java.lang.String, java.lang.String)
     */
    @Override
    public void chargeDataMsg(String merchNo, String businessNo) {
        RLock lock = RedissonLockUtil.getChargeLock(merchNo, businessNo);
        if (lock.tryLock()) {
            try {
                String acountNo = "";
                if (merchNo.contains(RedisConstants.key_split_symbol)) {
                    String[] datas = merchNo.split(RedisConstants.key_split_symbol);
                    merchNo = datas[0];
                    acountNo = datas[1];
                }

                MerchCharge merchCharge = RedisUtil.getMerchCharge(merchNo, acountNo, businessNo);
                if (merchCharge == null) {
                    logger.error("商户充值保存失败，充值订单不存在，{}，{},{}", merchNo, acountNo, businessNo);
                    return;
                }
                if (this.saveChargeData(merchCharge)) {
                    RedisUtil.delMerchCharge(merchCharge);
                    RedisUtil.delQrBusinessNo(merchCharge.getOutChannel(),
                            acountNo, businessNo);
                    logger.info("商户充值保存成功，{}，{}", merchNo, businessNo);
                }
            } finally {
                lock.unlock();
            }
        }
    }

    /**
     * @param merchCharge
     * @return
     * @Description 保存商户充值以及增加相应的流水
     */
//	@Transactional
    private boolean saveChargeData(MerchCharge merchCharge) {

        merchCharge.setCrtDate(DateUtil.getCurrentTimeInt());
        //保存数据

        merchChargeDao.save(merchCharge);
        //增加商户余额以及可用余额流水
        RecordMerchBalDO rdMerchBal = payHandlerService.balForMerchChargeAdd(merchCharge, merchCharge.getAmount(),
                FeeType.merchCharge.id(), OrderType.charge.id());
        rdMerchBal.setCrtDate(merchCharge.getCrtDate());
        rdMerchBalDao.save(rdMerchBal);

        RecordMerchBalDO rdMerchAvailBal = payHandlerService.availBalForMerchChargeAdd(merchCharge,
                merchCharge.getAmount(), FeeType.merchCharge.id(), OrderType.charge.id());
        rdMerchAvailBal.setCrtDate(merchCharge.getCrtDate());
        rdMerchAvailBalDao.save(rdMerchAvailBal);

        return true;
    }

    /**
     * @param order
     * @return
     * @Description 保存扫码订单数据
     */
    @Transactional
    public boolean saveQrOrderData(Order order) {
        String merchNo = order.getMerchNo();
        // 商户信息
        Merchant merchant = merchantService.get(merchNo);
        Order orderqr = payOrderQrDao.getByBusinessNo(order.getBusinessNo());

        // 支付通道信息
        PayQrConfigDO payQrConfig = payQrConfigService.get(order.getOutChannel(), orderqr.getMobile());

        BigDecimal amount = order.getRealAmount();
        // 成本金额
//		if(payQrConfig.getCostRate() != null){
////			order.setCostAmount(ParamUtil.multBig(amount, payQrConfig.getCostRate()));
////		}else{
        order.setCostAmount(BigDecimal.ZERO);
////		}
        // 聚富代理金额
//        BigDecimal jfRate = null;
//		/*if(merchant.getHandRate() != null && (jfRate=merchant.getHandRate().get(order.getOutChannel())) != null){
//			jfRate = merchant.getHandRate().get(order.getOutChannel());
//		}*/
//		if(jfRate == null){
//			jfRate = payQrConfig.getJfRate();
//		}
        if (payQrConfig.getCostRate() != null) {
            order.setQhAmount(ParamUtil.multBig(amount, payQrConfig.getCostRate()));
        } else {
            order.setQhAmount(BigDecimal.ZERO);
        }
        // 商户代理金额
        BigDecimal feeRate = null;
        if (ParamUtil.isNotEmpty(merchant.getCoinRate())) {
            feeRate = merchant.getCoinRate().get(order.getOutChannel()).get("rate");
        }
        if (feeRate != null) {
            order.setAgentAmount(ParamUtil.multSmall(amount, feeRate));
        } else {
            order.setAgentAmount(BigDecimal.ZERO);
        }
        int crtDate = order.getCrtDate();
        if (ParamUtil.isNotEmpty(order.getMsg()) && order.getMsg().length() > 50) {
            order.setMsg(order.getMsg().substring(0, 50));
        }
        //设置清算状态
        order.setClearState(ClearState.succ.id());
        payOrderDao.save(order);
        //如果没有手续费用，直接返回
//        if (order.getAgentAmount().compareTo(BigDecimal.ZERO) == 0) {
//            return true;
//        }
        // 扣除商户余额以及可用余额流水
        RecordMerchBalDO rdMerchBal = payHandlerService.balForMerchSub(order, order.getQhAmount(),
                FeeType.merchPreHand.id(), OrderType.pay.id());
        rdMerchBal.setCrtDate(crtDate);
        rdMerchBalDao.save(rdMerchBal);

        RecordMerchBalDO rdMerchAvailBal = payHandlerService.availBalForMerchSubForQr(order, order.getQhAmount(),
                FeeType.merchPreHand.id(), OrderType.pay.id());
        rdMerchBal.setCrtDate(crtDate);
        rdMerchAvailBalDao.save(rdMerchAvailBal);


        // 增加平台资金账户余额以及可用余额流水
        RecordFoundAcctDO rdFoundAcct = payHandlerService.balForPlatAdd(order,
                order.getQhAmount().subtract(order.getAgentAmount()),
                FeeType.platIn.id(), OrderType.pay.id());
        rdFoundAcct.setCrtDate(crtDate);
        rdFoundAcctDao.save(rdFoundAcct);
        rdFoundAcct = payHandlerService.availBalForPlatAdd(order,
                order.getQhAmount().subtract(order.getAgentAmount()), FeeType.platIn.id(), OrderType.pay.id());
        rdFoundAcct.setCrtDate(crtDate);
        rdFoundAvailAcctDao.save(rdFoundAcct);

        // 增加商户代理余额以及可用余额流水
        if (order.getAgentAmount().compareTo(BigDecimal.ZERO) != 0) {
            rdFoundAcct = payHandlerService.balForAgentAdd(order, order.getAgentAmount(), merchant.getParentAgent(),
                    FeeType.agentIn.id(), OrderType.pay.id());
            rdFoundAcct.setCrtDate(crtDate);
            rdFoundAcctDao.save(rdFoundAcct);
            rdFoundAcct = payHandlerService.availBalForAgentAdd(order,
                    order.getQhAmount().subtract(order.getAgentAmount()), merchant.getParentAgent(),
                    FeeType.platIn.id(), OrderType.pay.id());
            rdFoundAcct.setCrtDate(crtDate);
            rdFoundAvailAcctDao.save(rdFoundAcct);
        }
        return true;
    }


    /* (非 Javadoc)
     * Description:
     * @see com.qh.pay.service.PayQrService#superChargeQr(java.lang.String, java.lang.String, java.lang.String, java
     * .lang.String)
     */
    @Override
    public R superChargeQr(String merchNo, String outChannel, String monAmount, String businessNo) {
        Merchant merchant = merchantService.get(merchNo);
        if (merchant == null) {
            return R.error("请检查商户号");
        }

        MerchCharge merchCharge = PayQrService.initMerchCharge(merchNo, outChannel, monAmount, businessNo);
        merchCharge.setClearState(ClearState.succ.id());
        merchCharge.setOrderState(OrderState.succ.id());
        merchCharge.setCrtDate(DateUtil.getCurrentTimeInt());
        RLock lock = RedissonLockUtil.getChargeLock(merchNo, businessNo);
        if (lock.tryLock()) {
            try {
                if (this.saveChargeData(merchCharge)) {
                    return R.ok("充值成功");
                }
            } finally {
                lock.unlock();
            }
        }
        return R.error("充值失败！");

    }

    /* (非 Javadoc)
     * Description:
     * @see com.qh.pay.service.PayQrService#syncOrder(com.qh.pay.api.Order, java.lang.String)
     */
    @Override
    public R syncOrder(Merchant merchant, Order order, String businessNo) {
        order.setBusinessNo(businessNo);
        Order payOrderLose = payOrderLoseDao.getByBusinessNo(businessNo, order.getOutChannel());
        if (payOrderLose == null) {
            logger.error("未找到掉单记录，{}，{}，{}", businessNo, order.getMerchNo(), order.getOutChannel());
            return R.error("未找到掉单记录");
        } else {
            payOrderLose.setOrderNo(order.getOrderNo());
            payOrderLose.setOrderState(OrderState.succ.id());
            payOrderLose.setMsg("手动同步");
            if (payOrderLose.getAmount().compareTo(order.getAmount()) != 0) {
                logger.error("金额不一致，{}，{}，订单实际金额：{}，掉单金额：{}", businessNo, order.getMerchNo(),
                        order.getRealAmount(), payOrderLose.getAmount());
                return R.error("金额不一致");
            }
            if (payOrderLose.getCrtDate() - order.getCrtDate() > 60 * 60 * 24) {
                logger.error("时间相差过大，订单创建时间：{}，掉单创建时间：{}", order.getCrtDate(), payOrderLose.getCrtDate());
                return R.error("时间相差过大");
            }
            if (ParamUtil.isNotEmpty(payOrderLose.getOrderNo()) && !payOrderLose.getOrderNo().equals(order.getOrderNo())) {
                logger.error("同步的订单号不一致，订单订单号：{}，掉单订单号：{}", order.getOrderNo(), payOrderLose.getOrderNo());
                return R.error("同步的订单号不一致");
            }
            BigDecimal jfRate = null;
			/*if(merchant.getHandRate() != null && (jfRate=merchant.getHandRate().get(order.getOutChannel())) != null){
				jfRate = merchant.getHandRate().get(order.getOutChannel());
			}*/
//			if(jfRate == null){
//				PayQrConfigDO payQrConfig = payQrConfigService.get(order.getOutChannel(), order.getMerchNo(),order
//				.getPayMerch());
//				if(payQrConfig != null){
//					jfRate = payQrConfig.getJfRate();
//				}
//			}
            if (jfRate != null) {
                payOrderLose.setQhAmount(ParamUtil.multBig(payOrderLose.getAmount(), jfRate));
            } else {
                payOrderLose.setQhAmount(BigDecimal.ZERO);
            }
            payOrderLoseDao.update(payOrderLose);
            order.setOrderState(payOrderLose.getOrderState());
        }
        return R.ok(Constant.result_msg_ok);
    }

}
